/*
 *  Class name: EntityDataModel.java
 *  Version: 1.0
 *  Date: 21.11.2013
 */
package cz.muni.fi.pa165.soccergamesarchive.datamodels;

import cz.muni.fi.pa165.soccergamesarchive.structures.FilterValue;
import cz.muni.fi.pa165.soccergamesarchive.structures.RestrictionOperator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.faces.context.FacesContext;
import org.ajax4jsf.model.DataVisitor;
import org.ajax4jsf.model.ExtendedDataModel;
import org.ajax4jsf.model.Range;
import org.ajax4jsf.model.SequenceRange;
import org.richfaces.component.SortOrder;
import org.richfaces.model.Arrangeable;
import org.richfaces.model.ArrangeableState;
import org.richfaces.model.FilterField;
import org.richfaces.model.SortField;

import static javax.faces.context.FacesContext.getCurrentInstance;

/**
 *
 * @author Michal Ďuriš
 * @param <E> sga entities
 */
public abstract class EntityDataModel<E> extends ExtendedDataModel<E> implements
        Arrangeable {

    private int rowIndex;

    private Object rowKey;
    private ArrangeableState arrangeableState;

    protected List<E> dataList;

    protected final Map<String, Object> filterMap = new HashMap<>();
    protected final Map<String, RestrictionOperator> filterRestrictionOperatorsMap = new HashMap<>();

    protected final Map<String, Boolean> orderMap = new HashMap<>();

    public EntityDataModel() {
        super();
    }

    protected abstract List<E> getArrangedList(int offset, int limit);

    protected abstract int getAllArrangedItemsCount();

    protected abstract E getEntityByID(Object id);

    protected abstract Long getID(E entity);

    @Override
    public void arrange(FacesContext context, ArrangeableState state) {
        arrangeableState = state;
    }

    @Override
    public void setRowKey(Object key) {
        rowKey = key;
    }

    @Override
    public Object getRowKey() {
        return rowKey;
    }

    @Override
    public boolean isRowAvailable() {
        return rowKey != null;
    }

    @Override
    public void walk(FacesContext context, DataVisitor visitor, Range range,
                     Object argument) {

        SequenceRange sequenceRange = (SequenceRange) range;

        int firstRowIndex = 0;
        int totalRows = 1;

        if (sequenceRange.getFirstRow() >= 0 && sequenceRange.getRows() > 0) {
            firstRowIndex = sequenceRange.getFirstRow();
            totalRows = sequenceRange.getRows();
        }

        dataList = getArrangedList(firstRowIndex, totalRows);

        for (E e : dataList) {
            visitor.process(context, getID(e), argument);
        }
    }

    @Override
    public int getRowCount() {
        return getAllArrangedItemsCount();
    }

    @Override
    public E getRowData() {

        if (isRowAvailable()) {
            return getEntityByID(rowKey);
        }

        System.err.println(getClass().getSimpleName()
                           + "->getRowData->"
                           + "Call a method with unavailable row key"
                           + "RowKey value: " + rowKey);
        return null;
    }

    @Override
    public int getRowIndex() {
        return rowIndex;
    }

    @Override
    public void setRowIndex(int rowIndex) {
        this.rowIndex = rowIndex;
    }

    @Override
    public Object getWrappedData() {
        return dataList;
    }

    @Override
    public void setWrappedData(Object data) {
        throw new UnsupportedOperationException("Not supported.");
    }

    public Map<String, Object> getFilterMap() {

        if (arrangeableState != null) {

            FacesContext facesContext = getCurrentInstance();

            for (FilterField filterField : arrangeableState.getFilterFields()) {

                filterMap.put(
                        (String) filterField.getFilterExpression().getValue(
                                facesContext.getELContext()),
                        filterField.getFilterValue());
            }
        }

        return filterMap;
    }

    protected Map<String, Object> getCompleteFilterMap() {

        Map<String, Object> completeFilterMap = new HashMap<>(getFilterMap());

        if (!filterRestrictionOperatorsMap.isEmpty()) {

            for (Map.Entry<String, Object> entry : completeFilterMap.entrySet()) {

                if (filterRestrictionOperatorsMap.containsKey(entry.getKey())) {

                    completeFilterMap.put(entry.getKey(),
                            new FilterValue(entry.getValue(),
                                    filterRestrictionOperatorsMap.get(entry
                                            .getKey())));
                } else {

                    //TODO: LOGGER
                    System.out.println(getClass().getName()
                                       + ": No filter map key -> restriction operator mapping."
                                       + " Operator set to default AND.");

                    completeFilterMap.put(entry.getKey(), new FilterValue(entry
                            .getValue(), RestrictionOperator.AND));
                }
            }
        } else {

            for (Map.Entry<String, Object> entry : completeFilterMap.entrySet()) {
                completeFilterMap.put(entry.getKey(), new FilterValue(entry
                        .getValue(),
                        RestrictionOperator.AND));
            }
        }

        return completeFilterMap;

    }

    public Map<String, RestrictionOperator> getFilterRestrictionOperatorsMap() {
        return filterRestrictionOperatorsMap;
    }

    public Map<String, Boolean> getOrderMap() {

        Map<String, Boolean> completeOrderMap = new HashMap<>(orderMap);

        if (arrangeableState != null) {

            FacesContext facesContext = getCurrentInstance();

            for (SortField sortField : arrangeableState.getSortFields()) {

                if (sortField.getSortOrder() != SortOrder.unsorted) {

                    completeOrderMap.put((String) sortField.getSortBy()
                            .getValue(facesContext.getELContext()), sortField
                            .getSortOrder() == SortOrder.ascending ? Boolean.TRUE
                                                                    : Boolean.FALSE);
                }
            }
        }

        return completeOrderMap;
    }

}
